package com.report;

import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.config.ConfigureBuilder;
import com.deepoove.poi.template.MetaTemplate;
import com.deepoove.poi.template.run.RunTemplate;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.jxls.area.Area;
import org.jxls.area.CommandData;
import org.jxls.builder.AreaBuilder;
import org.jxls.command.EachCommand;
import org.jxls.transform.Transformer;
import org.jxls.util.JxlsHelper;
import org.springframework.core.io.ClassPathResource;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*;

public class TestReportDemo {


    @Resource
    private DataSource dataSource;
    @Resource
    private ReportComponent reportFuncComponent;

    private void test() throws IOException {
        ClassPathResource classPathResource = new ClassPathResource("report/xxx.xlsx");
        byte[] generate = this.generate(IOUtils.toByteArray(classPathResource.getInputStream()), new HashMap<>(), new HashMap<>(), TemplateType.EXCEL);
    }

    /**
     * 从fileBytes读取模板，将sqlParam作为SQL的查询条件，directMap作为直接参与写入模板的对象。
     *
     * @param fileBytes 模板的fileBytes。请注意，在这个系统中绝大多数交互都是通过byte[]。相对于流，它有可重复构造流的能力，在很多场景下这是很实用的。
     * @param sqlParam sqlParam是用来作为SQL语句的查询条件变量。所以它的key和value都一定是String。它会被用于
     *     context.loadDataSets(TemplateType.EXCEL);
     *     解析generic_report_dataset表中的SQL的查询条件:${k},从sqlParam中取相应的k的value替换。然后进行查询，比如有SQL数据集名称是myADataSets，内容是select
     *     * from a where f='${testKey}',
     *     sqlParam中有testKey:testValue,testKey2:testValue2,则在loadDateSets过程中，将会执行select * from a
     *     where f='testValue'
     * @param directMap
     *     directMap是用来作为直接替换到模板中的数据，它在loadDataSets后执行。所以会覆盖掉loadDataSets的执行结果，比如在generic_report_dataset表中有myADataSets，但是在directMap中也myADataSets，
     *     则会以directMap的myADataSets为准。directMap的key是一个字符串，但value可以是一个List<Map<String,Object>>，也可以是一个String.
     *     我们知道构造Map<String,Object>较为困难，从对象转为Map可以使用 Map<String,Object>
     *     objMap=jsonHelper.parse(jsonHelper.toJsonString(OBJ),new
     *     TypeReference<Map<String,Object>>() {});
     * @return null if ERROR
     */
    public  byte[] generate(
            byte[] fileBytes, Map<String, String> sqlParam, Map<String, Object> directMap , TemplateType templateType){
        //判断当前模版是excel 还是 word 模版
        List<String> nameList = new ArrayList<>();
        try (ByteArrayInputStream inputStream = new ByteArrayInputStream(fileBytes);
             ByteArrayOutputStream outputStream = new ByteArrayOutputStream()){
            if (TemplateType.EXCEL.equals(templateType)) {
                JxlsHelper helper = JxlsHelper.getInstance();
                Transformer transformer = helper.createTransformer(inputStream, outputStream);
                AreaBuilder areaBuilder = helper.getAreaBuilder();
                areaBuilder.setTransformer(transformer);
                List<Area> xlsAreaList = areaBuilder.build();
                //$.htValuationReport(#params.valuationDate,#params.counterPartyId,null) 模版直接调用方法案例
                //realizedBenefitSet 模版不传参数数据集案例
                //stdOptionValue.?[counterPartyId==#params.counterPartyId and valuationDate==#params.valuationDate and contractStatus=='0'] 模版传参数数据集案例
                if (CollectionUtils.isNotEmpty(xlsAreaList)) {
                    //遍历多个sheet页
                    xlsAreaList.forEach(item -> {
                        //遍历一个sheet页中的多个数据集或方法名称
                        List<CommandData> commandDataList = Optional.ofNullable(item.getCommandDataList()).orElse(Lists.newArrayList());
                        commandDataList.forEach(command -> {
                            EachCommand eachCommand = (EachCommand) command.getCommand();
                            String items = eachCommand.getItems();
                            if (StringUtils.isNoneBlank(items) && !items.contains("$")) {
                                String[] split = items.split("\\.");
                                if (split.length > 0) {
                                    nameList.add(split[0]);
                                }
                            }
                        });
                    });
                }
                transformer.dispose();
            }else if (TemplateType.WORD.equals(templateType)){
                ConfigureBuilder builder = Configure.builder();
                builder.useSpringEL();
                XWPFTemplate compile = XWPFTemplate.compile(inputStream, builder.build());
                List<MetaTemplate> elementTemplates = Optional.ofNullable(compile.getElementTemplates()).orElse(Lists.newArrayList());
                //_.def("S1",xingyeStdSwapTrade.^[contractId==#params.contractId])
                elementTemplates.forEach(item -> {
                    if (item instanceof RunTemplate) {
                        RunTemplate elementTemplate = (RunTemplate)item;
                        String tagName = elementTemplate.getTagName();
                        if (StringUtils.isNoneBlank(tagName) && tagName.contains("def") && ! tagName.contains("$")) {
                            String[] split = tagName.split(",");
                            if (split.length > 1) {
                                String[] split1 = split[1].split("\\.");
                                nameList.add(split1[0].replace(")", ""));
                            }
                        }
                    }
                });
                compile.close();
            }
        }catch (Exception e) {
        }
        // 构建数据源context
        TemplateContext context = new TemplateContext(dataSource, sqlParam);
        if (CollectionUtils.isNotEmpty(nameList)) {
            context.loadDataSets(templateType,nameList);
        }
        // 加载后，将directMap中内容覆盖到context._inner_context中去，请注意，是覆盖。
        for (String k : directMap.keySet()) {
            Object v = directMap.get(k);
            context.put(k, v);
        }
        context.putFunctionComponent(reportFuncComponent);
        byte[] bytes;
        // 获取ExcelEngine
        TemplateEngine engine = TemplateEngineFactory.createTemplateEngine(templateType);
        // 处理并写道字节输出流
        try(ByteArrayInputStream inputStream = new ByteArrayInputStream(fileBytes);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
            engine.processTemplate(inputStream, byteArrayOutputStream, context);
            bytes = byteArrayOutputStream.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException("报表引擎生成报表错误,请联系开发人员");
        }finally {
            context.clearContext();
        }
        return bytes;
    }
    public TemplateContext generateContext(Map<String, String> sqlParam ,TemplateType templateType){
        // 构建数据源context
        TemplateContext context = new TemplateContext(dataSource, sqlParam);
        // 此处加载全部 generic_report_dataset表中的数据到context._inner_context中，并且在此处就会对sql进行查询，但catch了所有异常。
        context.loadDataSets(templateType, null);
        return context;
    }
}
